package com.sunday.common.statemachine.study.e12_Using_Extended_State;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.StateMachineEventResult;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;

import java.util.Scanner;
import java.util.UUID;


@Slf4j
//@Component
public class MyCommandLineRunner1 implements CommandLineRunner {

    @Autowired
    private StateMachine<OrderStates, OrderEvents> stateMachine;

    @Override
    public void run(String... args) throws Exception {

        while (true) {
            Scanner scan = new Scanner(System.in);//实例化Scanner对象
            String input = scan.nextLine();//接受用户输入的字符串
            // 判断用户输入是否为"exit"
            if ("exit".equalsIgnoreCase(input)) {
                log.info("程序已退出");
                break;
            } else {
                log.info("你输入的文本是: {}", input);
                try {

                    /**
                     * Flux.just(message1, message2) 是使用 Project Reactor（Spring WebFlux 核心的响应式编程库）中的 Flux 类来创建一个包含两个消息 (message1 和 message2) 的流。Flux 是一个发布多个元素的异步流。在你给出的代码中，Flux.just(message1, message2) 创建了一个流，包含了两个事件消息，它们将被作为输入发送到状态机 (stateMachine.sendEvents(...))。
                     *
                     *                      解释
                     *                      Flux.just(message1, message2)：
                     *                      这会创建一个 Flux<OrderEvents> 流，该流包含两个元素 message1 和 message2。Flux.just() 是一个创建单个或多个元素流的工厂方法。它表示这两个消息对象将作为一个事件流进行处理。
                     *
                     *                      stateMachine.sendEvents(...)： stateMachine.sendEvents() 方法用于将事件（消息）发送到状态机。这里你传递的是一个 Flux<Message<OrderEvents>>，即包含多个消息的流。
                     *
                     *                      subscribe()：
                     *                      subscribe() 是触发流执行的操作，表示开始处理这个流中的事件。也就是说，当你调用 subscribe() 时，流中所有的事件会被依次触发，并且事件会按照流中的顺序被处理。
                     *
                     *                      产生的效果
                     *                      通过 Flux.just(message1, message2) 创建了一个包含两个事件消息的流。这两个消息将依次发送到状态机。
                     *                      stateMachine.sendEvents(Flux.just(message1, message2)) 会将 message1 和 message2 传递到状态机，并且每个消息会触发一次事件处理。
                     *                      调用 subscribe() 后，Flux 会被消费，message1 会首先被处理，然后是 message2。
                     *                      状态机会根据每个消息触发相应的事件。由于 Flux 是异步的，因此这两个消息是并发处理的（虽然在这个例子中，它们按顺序处理，Flux.just() 会确保消息按顺序流动）。
                     *                      异步处理：
                     *                      在 Flux.just(message1, message2) 中，这两个消息是异步的。它们不会阻塞等待上一个消息的处理结果。这意味着，尽管 message1 和 message2 是按顺序发送的，它们可能会同时进行处理。
                     *                      如果你希望这两个事件按照顺序依次处理，可以使用类似 concatMap() 这样的操作符来确保顺序执行。
                     *                      可能的变更：
                     *                      如果你有多个事件，Flux.just() 会将它们作为流传递给状态机，状态机会按顺序处理这些消息。如果消息处理有耗时的操作，或者事件需要在不同线程处理，事件的顺序执行可能会受到异步执行的影响。
                     *
                     *                      举个例子
                     *                      假设 stateMachine.sendEvents(Flux.just(message1, message2)) 发送的是订单事件：
                     *
                     *                      message1 可能触发订单的 CHECK_STOCK 事件，状态机会处理库存检查。
                     *                      message2 可能触发订单的 PAYMENT 事件，状态机会处理支付状态。
                     *                      由于 Flux 是异步流，状态机将处理 message1 和 message2 的事件，顺序上可能是先 message1 后 message2，但在底层处理过程中，这两个事件可能并行执行，具体取决于状态机的配置和执行方式。
                     *
                     *                      总结
                     *                      Flux.just(message1, message2) 将创建一个流，流中包含两个事件消息 message1 和 message2。
                     *                      这些消息将被传递给状态机处理，并按顺序触发对应的事件。
                     *                      subscribe() 会触发事件流的处理，可以异步处理多个事件。
                     */


                    Message<OrderEvents> message1 = MessageBuilder
                            .withPayload(OrderEvents.TS2)
                            .setHeader("OrderId", UUID.randomUUID())
                            .setHeader("stockAvailable", true)
//                            .setHeader(StateMachineMessageHeaders.HEADER_DO_ACTION_TIMEOUT, 5000)
                            .build();

                    Message<OrderEvents> message2 = MessageBuilder
                            .withPayload(OrderEvents.TS22)
                            .setHeader("OrderId", UUID.randomUUID())
                            .setHeader("stockAvailable", true)
//                            .setHeader(StateMachineMessageHeaders.HEADER_DO_ACTION_TIMEOUT, 5000)
                            .build();

                    Message<OrderEvents> message3 = MessageBuilder
                            .withPayload(OrderEvents.TS32)
                            .setHeader("OrderId", UUID.randomUUID())
                            .setHeader("stockAvailable", true)
//                            .setHeader(StateMachineMessageHeaders.HEADER_DO_ACTION_TIMEOUT, 5000)
                            .build();


                    Flux<StateMachineEventResult<OrderStates, OrderEvents>> results = stateMachine.sendEvents(Flux.just(message1, message2, message3));

                    results.subscribe();

                } catch (Exception e) {
                    log.error("无法识别的事件: {} , 异常: {}", input, e.toString());
                }
            }

        }

    }

    /**
     * https://docs.spring.io/spring-statemachine/docs/4.0.0/reference/index.html#using-eventtrigger
     * 用EventTrigger
     * EventTrigger是最有用的触发器，因为它允许您 通过向状态机发送事件来直接与状态机交互。这些 事件也称为信号。您可以向过渡添加触发器 通过在配置期间将状态与其关联。 以下示例显示了如何执行此操作：
     *
     * @Autowired
     * StateMachine<String, String> stateMachine;
     *
     * void signalMachine() {
     * 	stateMachine
     * 		.sendEvent(Mono.just(MessageBuilder
     * 			.withPayload("E1").build()))
     * 		.subscribe();
     *
     * 	Message<String> message = MessageBuilder
     * 			.withPayload("E2")
     * 			.setHeader("foo", "bar")
     * 			.build();
     * 	stateMachine.sendEvent(Mono.just(message)).subscribe();
     * }
     *
     * 无论您是发送一个事件还是多个事件，结果始终是一个序列 的结果。之所以如此，是因为在存在多个 reqions 的情况下，结果将 从这些区域中的多台计算机返回。这是 with 方法，该方法给出结果列表。方法 本身只是一个语法 Sugar Collecting As 列表。如果有 只有一个地区，此列表包含一个结果。sendEventCollectFlux
     *
     * Message<String> message1 = MessageBuilder
     * 	.withPayload("E1")
     * 	.build();
     *
     * Mono<List<StateMachineEventResult<String, String>>> results =
     * 	stateMachine.sendEventCollect(Mono.just(message1));
     *
     * results.subscribe();
     *
     * 在订阅返回的 flux 之前，什么都不会发生。从 StateMachineEventResult 中了解更多信息。
     *      前面的示例通过构造 wrapping 来发送事件 a 并订阅返回的结果。 让 我们向事件添加任意的额外信息，然后该信息可见 到实施操作的时间（例如）。MonoMessageFluxMessageStateContext
     *
     *      消息标头通常会一直传递，直到机器运行 完成特定事件。例如，如果事件导致 transition 转换为 state 中，该 state 具有匿名 transition 到 state 中，原始事件可用于 state 中的操作或守卫。ABB
     *      也可以发送消息，而不仅仅是发送 一个带有 .FluxMono
     *
     *      Message<String> message1 = MessageBuilder
     *      .withPayload("E1")
     *      .build();
     *      Message<String> message2 = MessageBuilder
     *      .withPayload("E2")
     *      .build();
     *
     *      Flux<StateMachineEventResult<String, String>> results =
     *      stateMachine.sendEvents(Flux.just(message1, message2));
     *
     *      results.subscribe();
     *
     */


}
